Skip to content

fix(ember): Stop retaining component render payloads in beforeEntries - #23052

Open
yashschandra wants to merge 1 commit into
getsentry:developfrom
yashschandra:fix/ember-render-instrumentation-leak
Open

fix(ember): Stop retaining component render payloads in beforeEntries#23052
yashschandra wants to merge 1 commit into
getsentry:developfrom
yashschandra:fix/ember-render-instrumentation-leak

Conversation

@yashschandra

Copy link
Copy Markdown

Before submitting a pull request, please take a look at our
Contributing guidelines and verify:

  • If you've added code that should be tested, please add tests.
  • Ensure your code lints and the test suite passes (yarn lint) & (yarn test).
  • Link an issue if there is one related to your pull request. If no issue is linked, one will be auto-generated and linked.

Problem

_instrumentComponents subscribes to Ember's render.component (and optionally render.getComponentDefinition) instrumentation and stores { payload, now } into a module-scope beforeEntries map keyed by payload.object (a per-component-instance GUID). processComponentRenderAfter reads the entry but never removes it, so every instrumented component render is retained for the lifetime of the page.

Since the render payload references the component instance (view), this pins each destroyed component and everything it references. In our Ember app (Customer.io Journeys) this retained every destroyed message-composer graph when users switched between wizard steps — ~34 MB of unreclaimable heap per step switch, measured in Chrome with --js-flags=--expose-gc and confirmed via heap-snapshot retainer paths ending at the instrumentation subscriber's beforeEntries closure:

(GC roots) → module context → subscribers → before closure → beforeEntries
  → entry → payload → view (component) → parent component graph (~34 MB / cycle)

Fix

  • Delete the entry in processComponentRenderAfter once the render completes.
  • Convert RenderEntries from an index-signature object to a Map (the repo's no-dynamic-delete oxlint rule disallows delete with dynamic keys, and a Map is the natural fit for GUID keys).
  • Export _processComponentRenderBefore / _processComponentRenderAfter (following the existing _-prefixed pattern used by instrumentEmberAppInstanceForPerformance) and add unit tests covering: entry removal on completion (both below and above minimumComponentRenderDuration), and that an after without a matching before leaves other in-flight entries untouched.

Verified in the affected app: with this change all destroyed component graphs become collectable and back-to-back heap snapshots across step-switch cycles show only ~0.5 MB/cycle of JIT-code growth instead of ~34 MB/cycle of retained objects.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@yashschandra
yashschandra requested a review from a team as a code owner August 5, 2026 10:22
@yashschandra
yashschandra requested review from nicohrubec and s1gr1d and removed request for a team August 5, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant